// /////////////////////////////////////////////////////////////////////////////
//
// Name:            Communicator.cpp
// Author:          Michael Bartsch
//
// Desc :           This file contains the CCommunicator class. This class
//                  is responsible for communicating with Aia.
//
// License:         Copyright (C) 2008-2009 Michael Bartsch
//
//                  This program is free software: you can redistribute it
//                  and/or modify it under the terms of the GNU General Public
//                  License as published by the Free Software Foundation,
//                  either version 3 of the License, or (at your option) any
//                  later version.
//
//                  This program is distributed in the hope that it will be
//                  useful, but WITHOUT ANY WARRANTY; without even the implied
//                  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//                  PURPOSE. See the GNU General Public License for more
//                  details.
//
//                  You should have received a copy of the GNU General Public
//                  License along with this program. If not, see
//                  <http://www.gnu.org/licenses/>.
//
// /////////////////////////////////////////////////////////////////////////////

// Include required header file(s).
#include "Communicator.h"


// Communicator
// Constructor.
CCommunicator::CCommunicator()
: serialPort(NULL), isSerialConnection(false), isReadThread(false), selectedDevice(-1)
{
}

// Deconstructor.
CCommunicator::~CCommunicator()
{
    // Disconnect.
    delete threadRead;
    delete serialPort;
}

// CCommunicator singleton.
CCommunicator* CCommunicator::pInstance = 0;

CCommunicator* CCommunicator::Instance()
{
	if(pInstance == 0)
	  pInstance = new CCommunicator;

	return pInstance;
}

// Reads data from the serial connection.
void CCommunicator::serialReader()
{
    while(true)
    {
       // Do we have a serial connection?
       if(isSerialConnection)
       {
          // Reset some variables.
          char mainBuffer[128];
          memset(mainBuffer, 0, 128);

          bool isSerialCommand = false;
          int locStartTag, locEndTag;

          // Read from the Arduino and check for commands.
          while(true)
          {
             // Read from the Arduino and add the output to the main buffer.
             char buffer[64];
             memset(buffer, 0, 64);

             serialPort->read_some(boost::asio::buffer(buffer, 64));

             strcat (mainBuffer, buffer);

             // Examine if a command has been sent.
             bool hasStartTag = false;

             for(int i = 0; i < 64; i++)
             {
                if(hasStartTag || mainBuffer[i] == '<')
                {
                   hasStartTag = true;

                   if(mainBuffer[i] == '<')
                     locStartTag = i;

                   if(mainBuffer[i] == '>')
                   {
                      isSerialCommand = true;
                      locEndTag = i;
                      break;
                   }
                }

                // If the main buffer has filled reset it.
                if(i == 63 && mainBuffer[63] != 0)
                 memset(buffer, 0, 64);
              }

              // If a command has been sent break out of the loop.
              if(isSerialCommand)
                break;
          }

          // If we have a command examine it.
          if(isSerialCommand)
          {
             // Check for the namespace.
             if(strncmp(mainBuffer+locStartTag+1, "in", 2) == 0)
             {
                // Check for the module.
                if(strncmp(mainBuffer+locStartTag+4, "sonar", 5) == 0)
                {
                    // Determine the value of the sonar.
                    int distance;
                    char buffer[3];
                    memset(buffer, 0, 3);

                    for(int i = 0; i < 3; i++)
                    {
                       if(mainBuffer[locStartTag+10+i] >= '0' && mainBuffer[locStartTag+10+i] <= '9')
                       buffer[i] = mainBuffer[locStartTag+10+i];

                       else break;   // Error
                    }

                    distance = atoi(buffer);

                    // Submit received data to the AI Core.
                    CAICore::Instance()->setNodesSonar(distance);
                    CAICore::Instance()->setIsWaiting(false);
                }

                else if(strncmp(mainBuffer+locStartTag+4, "done", 4) == 0)
                  CAICore::Instance()->setIsWaiting(false);
             }
          }

          // Yield.
          threadRead->yield();
       }
    }
}

// Writes data to the serial stream.
void CCommunicator::serialWrite(const char *command)
{
   if(isSerialConnection)
   {
	  char buffer[64];
      strcpy(buffer, command);
      serialPort->write_some(boost::asio::buffer(buffer, 64));
   }
}

// Reconnects to a (new) serial device.
void CCommunicator::serialConnect(int device)
{
   // Suspend the running thread.
   if(isReadThread)
   {
      boost::xtime xt;
      boost::xtime_get(&xt, boost::TIME_UTC);
      xt.sec += 1;
      boost::thread::sleep(xt);

      threadRead->sleep(xt);
   }

   // Close the serial connection.
   if(isSerialConnection)
     serialPort->close();

   // Connect to the given device.
   isSerialConnection = true;
   selectedDevice = device;

   try
   {
      char buffer[2];
      memset(buffer, 0, 2);

      #ifdef __OS_WINDOWS__
      sprintf(buffer, "COM%d", selectedDevice);
      serialPort = new boost::asio::serial_port(ioService, buffer);
      #elif __OS_LINUX__
      sprintf(buffer, "/dev/ttyUSB%d", selectedDevice);
      serialPort = new boost::asio::serial_port(ioService, buffer);
      #endif
   }
   catch (std::exception& e)
   {
      isSerialConnection = false;
   }

   // Configure the serial connection if we have it.
   if(isSerialConnection)
   {
      boost::asio::serial_port_base::baud_rate baudRate(9600);
      serialPort->set_option(baudRate);

      if(!isReadThread)
      {
         threadRead = new boost::thread(boost::bind(&CCommunicator::serialReader, this));
         isReadThread = true;
      }
   }
}

// Gets the currently active device.
int CCommunicator::getSelectedDevice()
{
    return selectedDevice;
}
// End of File
